#ifndef cathlibcpp_string_H
#define cathlibcpp_string_H

// File:       string.h
// Author:     (c) Miles Sabin, 1997
// Purpose:    approximation to ANSI C++ string class

// Change log:
//  31/12/97   Corrected compare operation so that the length is checked if
//               the initial prefix is equal. (by Alexander Thoukydides)


#ifndef included_string_H
#define included_string_H
#include <string.h>              // reference to CLib's <string.h>
#endif

#ifndef cathlibcpp_bool_H
#include "bool.h"
#endif

#ifndef cathlibcpp_config_H
#include "config.h"
#endif

#ifndef cathlibcpp_iosfwd_H
#include "iosfwd.h"              // for istream, ostream
#endif

#ifndef cathlibcpp_iterator_H
#include "iterator.h"
#endif

#ifndef cathlibcpp_newcasts_H
#include "newcasts.h"
#endif


class basic_string_char;
typedef basic_string_char string;


struct char_traits_char
{
  // types
  typedef char       char_type;
  typedef int        int_type;
  typedef int        off_type;
  typedef int        pos_type;
  typedef int        state_type;

  static void assign(char_type& c1, char_type const& c2)
    { c1 = c2; }

  static bool eq(char_type const& c1, char_type const& c2)
    { return c1 == c2; }

  static bool lt(char_type const& c1, char_type const& c2)
    { return c1 < c2; }

  static int compare(char_type const* s1, char_type const* s2, size_t n)
    { return ::memcmp(s1, s2, n); }

  static size_t length(char_type const* s)
    { return ::strlen(s); }

  static char_type const* find(char_type const* s, int n, char_type a)
    { return reinterpret_cast(char const*, ::memchr(s, a, n)); }

  static char_type* move(char_type* s1, char_type const* s2, size_t n)
    { return reinterpret_cast(char*, ::memmove(s1, s2, n)); }

  static char_type* copy(char_type* s1, char_type const* s2, size_t n)
    { return reinterpret_cast(char*, ::memcpy(s1, s2, n)); }

  static char_type* assign(char_type* s1, size_t n, char_type const a)
    { return reinterpret_cast(char*, ::memset(s1, a, n)); }

  static int_type not_eof(char_type c)
    { return c&0x7f; }

  static char_type to_char_type(int_type c)
    { return c; }

  static int_type  to_int_type(char_type c)
    { return c; }

  static bool eq_int_type(int_type c1, int_type c2)
    { return c1 == c2; }

  static state_type get_state(pos_type)
    { return 0; }

  static inline int_type eof()
    { return -1; }

  static inline char eos()
    { return 0; }
};


class basic_string_char
{
  public:

    // types
    typedef char_traits_char                                 traits;
    typedef size_t                                           size_type;
    typedef ptrdiff_t                                        difference_type;
    typedef char*                                            iterator;
    typedef char const*                                      const_iterator;
    typedef reverse_iterator<char*, char, char&>             rev_iterator;
    typedef reverse_iterator<char const*, char, char const&> const_rev_iterator;

    enum { npos = -1 };

    // constructors
    basic_string_char();
    basic_string_char(basic_string_char const& s);
    basic_string_char(basic_string_char const& s, size_type pos, size_type n = npos);
    basic_string_char(char const* s);
    basic_string_char(char const* s, size_type n);
    basic_string_char(size_type n, char c);
    basic_string_char(char const* first, char const* last);
    ~basic_string_char();

    // accessors
    const_iterator begin() const;
    const_iterator end() const;
    const_rev_iterator rbegin() const;
    const_rev_iterator rend() const;

    char operator[](size_type pos) const;
    char at(size_type pos) const;

    size_type size() const;
    size_type length() const;
    size_type max_size() const;
    size_type capacity() const;
    bool empty() const;

    size_type find(basic_string_char const& s, size_type pos = 0) const;
    size_type find(char const* s, size_type pos = 0) const;
    size_type find(char const* s, size_type pos, size_type n) const;
    size_type find(char c, size_type pos = 0) const;

    size_type rfind(basic_string_char const& s, size_type pos = npos) const;
    size_type rfind(char const* s, size_type pos = npos) const;
    size_type rfind(char const* s, size_type pos, size_type n) const;
    size_type rfind(char c, size_type pos = npos) const;

    size_type find_first_of(basic_string_char const& s, size_type pos = 0) const;
    size_type find_first_of(char const* s, size_type pos = 0) const;
    size_type find_first_of(char const* s, size_type pos, size_type n) const;
    size_type find_first_of(char c, size_type pos = 0) const;

    size_type find_last_of(basic_string_char const& s, size_type pos = npos) const;
    size_type find_last_of(char const* s, size_type pos = npos) const;
    size_type find_last_of(char const* s, size_type pos, size_type n) const;
    size_type find_last_of(char c, size_type pos = npos) const;

    size_type find_first_not_of(basic_string_char const& s, size_type pos = 0) const;
    size_type find_first_not_of(char const* s, size_type pos = 0) const;
    size_type find_first_not_of(char const* s, size_type pos, size_type n) const;
    size_type find_first_not_of(char c, size_type pos = 0) const;

    size_type find_last_not_of(basic_string_char const& s, size_type pos = npos) const;
    size_type find_last_not_of(char const* s, size_type pos = npos) const;
    size_type find_last_not_of(char const* s, size_type pos, size_type n) const;
    size_type find_last_not_of(char c, size_type pos = npos) const;

    basic_string_char substr(size_type pos = 0, size_type n = npos) const;

    int compare(const basic_string_char& str) const;
    int compare(size_type pos1, size_type n1, const basic_string_char& str) const;
    int compare(size_type pos1, size_type n1, const basic_string_char& str, size_type pos2, size_type n2) const;
    int compare(const char* s) const;
    int compare(size_type pos1, size_type n1, const char* s, size_type n2 = npos) const;

    // mutators
    basic_string_char& operator=(basic_string_char const& rhs);
    basic_string_char& operator=(char const* rhs);
    basic_string_char& operator=(char rhs);

    iterator begin();
    iterator end();
    rev_iterator rbegin();
    rev_iterator rend();

    void resize(size_type n);
    void resize(size_type n, char c);
    void reserve(size_type sz);

    char& operator[](size_type pos);
    char& at(size_type pos);

    basic_string_char& operator+=(basic_string_char const& rhs);
    basic_string_char& operator+=(char const* rhs);
    basic_string_char& operator+=(char rhs);

    basic_string_char& append(basic_string_char const& s, size_type pos = 0, size_type n = npos);
    basic_string_char& append(char const* s);
    basic_string_char& append(char const* s, size_type n);
    basic_string_char& append(size_type n, char c);
    basic_string_char& append(char const* first, char const* last);

    basic_string_char& assign(basic_string_char const& s, size_type pos = 0, size_type n = npos);
    basic_string_char& assign(char const* s);
    basic_string_char& assign(char const* s, size_type n);
    basic_string_char& assign(size_type n, char c);
    basic_string_char& assign(char const* first, char const* last);

    basic_string_char& insert(size_type pos1, basic_string_char const& s, size_type pos2 = 0, size_type n = npos);
    basic_string_char& insert(size_type pos, char const* s);
    basic_string_char& insert(size_type pos, char const* s, size_type n);
    basic_string_char& insert(size_type pos, size_type n, char c);
    iterator insert(iterator p, char c);
    iterator insert(iterator p, size_type n, char c);
    void insert(iterator p, char const* first, char const* last);

    basic_string_char& erase(size_type pos = 0, size_type n = npos);
    basic_string_char& erase(iterator pos);
    basic_string_char& erase(iterator first, iterator last);

    basic_string_char& replace(size_type pos1, size_type n1, basic_string_char const& s, size_type pos2 = 0, size_type n2 = npos);
    basic_string_char& replace(size_type pos, size_type n1, char const* s, size_type n2);
    basic_string_char& replace(size_type pos, size_type n1, char const* s);
    basic_string_char& replace(size_type pos, size_type n, char c);
    basic_string_char& replace(iterator i1, iterator i2, basic_string_char const& str);
    basic_string_char& replace(iterator i1, iterator i2, char const* s, size_type n);
    basic_string_char& replace(iterator i1, iterator i2, char const* s);
    basic_string_char& replace(iterator i1, iterator i2, size_type n, char c);
    basic_string_char& replace(iterator i1, iterator i2, char const* j1, char const* j2);

    size_type copy(char* s, size_type n, size_type pos = 0);
    void swap(basic_string_char& x);

    // explicit conversion
    char const* c_str() const;
    char const* data() const;

  private:

    iterator replace_aux(size_type pos, size_type xlen, size_type n);

    void unshare();

    struct basic_string_char_rep
    {
      basic_string_char_rep(size_type n);
      ~basic_string_char_rep();

      bool is_shared() const;

      void set_size(size_type sz);

      basic_string_char_rep* add_reference();
      void remove_reference();

      char* begin_;
      char* end_;
      char* end_of_storage_;
      int ref_count_;

      static basic_string_char_rep* null_rep();
    };

    basic_string_char_rep* rep_;
};


void destroy(basic_string_char* t);

basic_string_char operator+(basic_string_char const& lhs, basic_string_char const& rhs);
basic_string_char operator+(char const* lhs, basic_string_char const& rhs);
basic_string_char operator+(basic_string_char const& lhs, char const* rhs);
basic_string_char operator+(char lhs, basic_string_char const& rhs);
basic_string_char operator+(basic_string_char const& lhs, char rhs);

bool operator==(basic_string_char const& lhs, basic_string_char const& rhs);
bool operator==(basic_string_char const& lhs, char const* rhs);
bool operator==(char const* lhs, basic_string_char const& rhs);

bool operator!=(basic_string_char const& lhs, basic_string_char const& rhs);
bool operator!=(basic_string_char const& lhs, char const* rhs);
bool operator!=(char const* lhs, basic_string_char const& rhs);

bool operator< (basic_string_char const& lhs, basic_string_char const& rhs);
bool operator< (basic_string_char const& lhs, char const* rhs);
bool operator< (char const* lhs, basic_string_char const& rhs);

bool operator> (basic_string_char const& lhs, basic_string_char const& rhs);
bool operator> (basic_string_char const& lhs, char const* rhs);
bool operator> (char const* lhs, basic_string_char const& rhs);

bool operator<=(basic_string_char const& lhs, basic_string_char const& rhs);
bool operator<=(basic_string_char const& lhs, char const* rhs);
bool operator<=(char const* lhs, basic_string_char const& rhs);

bool operator>=(basic_string_char const& lhs, basic_string_char const& rhs);
bool operator>=(basic_string_char const& lhs, char const* rhs);
bool operator>=(char const* lhs, basic_string_char const& rhs);

void swap(basic_string_char& lhs, basic_string_char& rhs);

basic_istream_char& operator>>(basic_istream_char& is, basic_string_char& str);
basic_ostream_char& operator<<(basic_ostream_char& os, basic_string_char const& str);
basic_istream_char& getline(basic_istream_char& is, basic_string_char& str, char delim);
basic_istream_char& getline(basic_istream_char& is, basic_string_char& str);


// Implementation of basic_string_char::basic_string_char_rep

inline basic_string_char::basic_string_char_rep* basic_string_char::basic_string_char_rep::add_reference()
  {
    ++ref_count_;
    return this;
  }

inline bool basic_string_char::basic_string_char_rep::is_shared() const
  { return ref_count_ > 1; }


// Implementation of basic_string_char

inline basic_string_char::basic_string_char()
  : rep_(basic_string_char_rep::null_rep())
  {}

inline basic_string_char::basic_string_char(basic_string_char const& s)
  : rep_(s.rep_->add_reference())
  {}

inline basic_string_char::const_iterator basic_string_char::begin() const
  { return rep_->begin_; }

inline basic_string_char::const_iterator basic_string_char::end() const
  { return rep_->end_; }

inline char basic_string_char::operator[](size_type pos) const
  { return rep_->begin_[pos]; }

inline char basic_string_char::at(size_type pos) const
  { return rep_->begin_[pos]; }

inline basic_string_char::size_type basic_string_char::size() const
  { return rep_->end_-rep_->begin_; }

inline basic_string_char::size_type basic_string_char::length() const
  { return size(); }

inline basic_string_char::size_type basic_string_char::capacity() const
  { return rep_->end_of_storage_-rep_->begin_-1; }

inline bool basic_string_char::empty() const
  { return size() == 0; }

inline basic_string_char::iterator basic_string_char::begin()
  {
    if(rep_->is_shared())
      unshare();
    return rep_->begin_;
  }

inline basic_string_char::iterator basic_string_char::end()
  {
    if(rep_->is_shared())
      unshare();
    return rep_->end_;
  }

inline char& basic_string_char::operator[](size_type pos)
  {
    if(rep_->is_shared())
      unshare();
    return rep_->begin_[pos];
  }

inline char& basic_string_char::at(size_type pos)
  {
    if(rep_->is_shared())
      unshare();
    return rep_->begin_[pos];
  }

inline char const* basic_string_char::c_str() const
  { return rep_->begin_; }

inline char const* basic_string_char::data() const
  { return (empty() ? 0 : c_str()); }


// Implementation of basic_string_char free fns

inline void destroy(basic_string_char* t)
{
  t->~basic_string_char();
}


inline bool operator==(basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) == 0;
}

inline bool operator==(basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) == 0;
}

inline bool operator==(char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) == 0;
}


inline bool operator!=(basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) != 0;
}

inline bool operator!=(basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) != 0;
}

inline bool operator!=(char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) != 0;
}


inline bool operator< (basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) < 0;
}

inline bool operator< (basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) < 0;
}

inline bool operator< (char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) > 0;
}


inline bool operator> (basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) > 0;
}

inline bool operator> (basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) > 0;
}

inline bool operator> (char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) < 0;
}


inline bool operator<=(basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) <= 0;
}

inline bool operator<=(basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) <= 0;
}

inline bool operator<=(char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) >= 0;
}


inline bool operator>=(basic_string_char const& lhs, basic_string_char const& rhs)
{
  return lhs.compare(rhs) >= 0;
}

inline bool operator>=(basic_string_char const& lhs, char const* rhs)
{
  return lhs.compare(rhs) >= 0;
}

inline bool operator>=(char const* lhs, basic_string_char const& rhs)
{
  return rhs.compare(lhs) <= 0;
}

inline void swap(basic_string_char& lhs, basic_string_char& rhs)
{
  lhs.swap(rhs);
}


inline basic_istream_char& getline(basic_istream_char& is, basic_string_char& str)
{
  return getline(is, str, '\n');
}

#endif
